#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Serialization;

namespace AbstractClass.Collections.Graph
{
    /// <summary>
    /// Represents a node in a <see cref="Graph&lt;T&gt;"/>.
    /// </summary>
    /// <typeparam name="T">The type of the data associated with the node.</typeparam>
    [Serializable]
    public class GraphNode<T> : ICloneable
    {
        [NonSerialized] private Graph<T> _parent;

        /// <summary>
        /// Initializes a new instance of the <see cref="GraphNode&lt;T&gt;"/> class.
        /// </summary>
        public GraphNode()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GraphNode&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="value">The value associated with the new <see cref="GraphNode&lt;T&gt;"/>.</param>
        public GraphNode(T value)
        {
            Value = value;
        }

        /// <summary>
        /// Gets or sets the owner <see cref="Graph&lt;T&gt;"/> of this instance.
        /// </summary>
        /// <value>The owner <see cref="Graph&lt;T&gt;"/>.</value>
        [XmlIgnore]
        public Graph<T> Owner
        {
            get { return _parent; }
            set { _parent = value; }
        }

        /// <summary>
        /// Gets or sets the value associated with this instance.
        /// </summary>
        /// <value>The value associated with this instance.</value>
        public T Value { get; set; }

        /// <summary>
        /// Gets the total number of incident edges and loops of this instance.
        /// </summary>
        /// <value>The degree of this instance.</value>
        public int Degree
        {
            get { return Edges.Count() + Loops.Count(); }
        }

        /// <summary>
        /// Gets all out-bound edges of this instance.
        /// </summary>
        /// <value>The out-bound edges of this instance.</value>
        public IEnumerable<GraphEdge<T>> OutBoundEdges
        {
            get
            {
                ReadOnlyCollection<GraphEdge<T>> edges = Owner.Edges;
                return edges.Where(graphEdge => graphEdge.Source.Equals(this));
            }
        }

        /// <summary>
        /// Gets all in-bound edges of this instance.
        /// </summary>
        /// <value>The in-bound edges of this instance.</value>
        public IEnumerable<GraphEdge<T>> InBoundEdges
        {
            get
            {
                ReadOnlyCollection<GraphEdge<T>> edges = Owner.Edges;
                return edges.Where(graphEdge => graphEdge.Destination.Equals(this));
            }
        }

        /// <summary>
        /// Gets all edges (out-bound and in-bound) of this instance.
        /// </summary>
        /// <value>The collection of all edges of this instance.</value>
        public IEnumerable<GraphEdge<T>> Edges
        {
            get
            {
                ReadOnlyCollection<GraphEdge<T>> edges = Owner.Edges;
                return edges.Where(graphEdge => graphEdge.Destination.Equals(this) || graphEdge.Source.Equals(this));
            }
        }

        /// <summary>
        /// Gets the directed edges of this instance.
        /// </summary>
        /// <value>The collection of all directed edges.</value>
        public IEnumerable<GraphEdge<T>> DirectedEdges
        {
            get { return Edges.Where(graphEdge => graphEdge.IsDirected); }
        }

        /// <summary>
        /// Gets the un-directed edges of this instance.
        /// </summary>
        /// <value>The collection of all un-directed edges.</value>
        public IEnumerable<GraphEdge<T>> UnDirectedEdges
        {
            get { return Edges.Where(graphEdge => !graphEdge.IsDirected); }
        }

        /// <summary>
        /// Gets the loops connected to this instance.
        /// </summary>
        /// <value>The collection of all loops.</value>
        public IEnumerable<GraphEdge<T>> Loops
        {
            get { return Edges.Where(graphEdge => graphEdge.Source.Equals(this) && graphEdge.Destination.Equals(this)); }
        }

        /// <summary>
        /// Gets the neighbor (connected) nodes of this instance.
        /// </summary>
        /// <value>The collection of all neighbor nodes of this instance.</value>
        public IEnumerable<GraphNode<T>> Neighbors
        {
            get { return GetConnectedNodes().Distinct(); }
        }

        /// <summary>
        /// Determines whether this instance is adjacent with the specified node.
        /// </summary>
        /// <param name="node">The node to check for adjacency.</param>
        /// <returns>
        /// 	<c>true</c> if this instance is adjacent with the specified node; otherwise, <c>false</c>.
        /// </returns>
        public bool IsAdjacentWith(T node)
        {
            return Neighbors.Select(gNode => gNode.Value).Contains(node);
        }

        /// <summary>
        /// Determines whether this instance is adjacent with the specified node.
        /// </summary>
        /// <param name="node">The node to check for adjacency.</param>
        /// <returns>
        /// 	<c>true</c> if this instance is adjacent with the specified node; otherwise, <c>false</c>.
        /// </returns>
        public bool IsAdjacentWith(GraphNode<T> node)
        {
            return Neighbors.Contains(node);
        }

        private IEnumerable<GraphNode<T>> GetConnectedNodes()
        {
            ReadOnlyCollection<GraphEdge<T>> edges = Owner.Edges;
            foreach (var graphEdge in edges)
            {
                if (graphEdge.Source.Equals(this) && !graphEdge.Destination.Equals(this))
                    yield return graphEdge.Destination;
                if (graphEdge.Destination.Equals(this) && !graphEdge.Source.Equals(this))
                    yield return graphEdge.Source;
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0}", Value);
        }

        #region Implementation of ICloneable

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            return MemberwiseClone();
        }

        #endregion
    }
}